package com.dynamic.mybatis.core.parser.xml.validator;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description mybatis 转 codeMirror,前端html codeMirror插件用来提示使用
 * @Author xs
 * @Date 2023/3/30 11:19
 */
public class MybatisSchemaCodeMirrorParser {

    public static Map<String, XmlSchemaElement> parse(InputStream schemaInputStream) {
        SAXReader saxReader = new SAXReader();
        Document document;
        try {
            document = saxReader.read(schemaInputStream);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
        return parseTree(document.getRootElement().elements());
    }

    private static Map<String, XmlSchemaElement> parseTree(List<Element> elements) {
        if (elements == null || elements.size() < 1) {
            return null;
        }
        Map<String, XmlSchemaElement> xmlSchemaElements = new HashMap<>();
        for (Element element : elements) {
            String tagName = element.getName();
            Attribute attribute;
            if (!tagName.equals("element") || (attribute = element.attribute("name")) == null) {
                continue;
            }
            String name = attribute.getValue();
            XmlSchemaElement xmlSchemaElement;
            if (!xmlSchemaElements.containsKey(name)) {
                xmlSchemaElement = new XmlSchemaElement();
                xmlSchemaElements.put(name, xmlSchemaElement);
            } else {
                xmlSchemaElement = xmlSchemaElements.get(name);
            }
            xmlSchemaElement.setAttrs(getAttributes(element));
            xmlSchemaElement.setChildren(getReferences(element));
        }
        return xmlSchemaElements;
    }


    public static Map<String, Object> getAttributes(Element element) {
        List<Element> elements = element.elements();
        if (elements == null || elements.size() < 1) {
            return null;
        }
        Map<String, Object> attributes = new LinkedHashMap<>();
        for (Element e : elements) {
            String tagName = e.getName();
            if ("attribute".equals(tagName)) {
                Attribute attribute = e.attribute("name");
                Attribute use = e.attribute("use");
                if (attribute != null) {
                    Object val;
                    if (use != null && "required".equals(use.getValue())) {
                        val = true;
                    } else if (e.elements() != null && e.elements().size() > 0) {
                        val = getAttributeEnums(e);
                    } else {
                        val = "";
                    }
                    attributes.put(attribute.getValue(), val);
                }
            } else {
                Map<String, Object> attrs = getAttributes(e);
                if (attrs != null) {
                    attributes.putAll(attrs);
                }
            }
        }
        Map<String, Object> attributes1 = attributes.entrySet().stream().sorted((a, b) -> {
            Object first = a.getValue();
            Object secOnd = b.getValue();
            return first != null ? 1 : -1;
        }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> x, LinkedHashMap::new));
        return attributes1;
    }


    public static List<String> getAttributeEnums(Element element) {
        List<Element> elements = element.elements();
        if (elements == null || elements.size() < 1) {
            return null;
        }
        List<String> enums = new ArrayList<>();
        for (Element e : elements) {
            String tagName = e.getName();
            if ("enumeration".equals(tagName)) {
                Attribute attribute = e.attribute("value");
                if (attribute != null) {
                    enums.add(attribute.getValue());
                }
            } else {
                List<String> enum1 = getAttributeEnums(e);
                if (enum1 != null) {
                    enums.addAll(enum1);
                }
            }
        }
        return enums;
    }

    public static List<String> getReferences(Element element) {
        List<Element> elements = element.elements();
        if (elements == null || elements.size() < 1) {
            return null;
        }
        List<String> references = new ArrayList<>();
        for (Element e : elements) {
            if (e.getName().equals("element")) {
                Attribute attribute = e.attribute("ref");
                if (attribute != null) {
                    references.add(attribute.getValue());
                }
            } else {
                List<String> refs = getReferences(e);
                if (refs != null) {
                    references.addAll(refs);
                }
            }
        }
        return references;
    }


    public static class XmlSchemaElement {
        private Map<String, Object> attrs;
        private List<String> children;

        public Map<String, Object> getAttrs() {
            return attrs;
        }

        public void setAttrs(Map<String, Object> attrs) {
            this.attrs = attrs;
        }

        public List<String> getChildren() {
            return children;
        }

        public void setChildren(List<String> children) {
            this.children = children;
        }
    }

    static class XmlSchemaValidator {

    }

    public static void main(String[] args) throws IOException {

        String schemaPackagePath = "com/dynamic/mybatis/core/parser/xml/validator/dms-mybatis-mapper.xsd";
        InputStream inputStream = new ClassPathResource(schemaPackagePath).getInputStream();

        Map<String, XmlSchemaElement> schema = MybatisSchemaCodeMirrorParser.parse(inputStream);

        String json = JSON.toJSONString(schema, JSONWriter.Feature.WriteMapNullValue);

        System.out.println(json);
    }
}